﻿using Microsoft.AspNet.SignalR;
using Microsoft.AspNet.SignalR.Hubs;
using System;
using System.Collections.Generic;
using System.Linq;
using ZSoft.PushServices.Net.DTO;
using ZSoft.PushServices.Net.Transport.ConnectionEvents;
using ZSoft.PushServices.Net.Transport.ConnectionEvents.EventsArsg;
using ZSoft.PushServices.Net.Transport.ConnectionEvents.Handlers;

namespace ZSoft.PushServices.Net.Transport
{
    public class InMemoryConnectionManager : IConnectionManager
    {
        private static Lazy<Dictionary<string, ClientInfo>> _lazyLoader =
            new Lazy<Dictionary<string, ClientInfo>>(() => new Dictionary<string, ClientInfo>(), true);

        public event DeviceConnectedHandler DeviceConnected;
        public event DeviceDisconnectedHandler DeviceDissconnected;
        public event DeviceReconnectedHandler DeviceReconnected;
        public event DeviceIdentifiedHandler DeviceIdentified;

        private static InMemoryConnectionManager Current
        {
            get
            {
                return ((GlobalHost.DependencyResolver.GetService(typeof(IConnectionManager))) as InMemoryConnectionManager);
            }
        }

        private static Dictionary<string, ClientInfo> Store
        {
            get
            {
                return _lazyLoader.Value;
            }
        }

        #region EventProtectors
        protected virtual void OnDeviceConnected(DeviceConnectionEventArgs e)
        {
            this.DeviceConnected?.Invoke(this, e);
        }

        protected virtual void OnDeviceDisconnected(DeviceConnectionEventArgs e)
        {
            this.DeviceDissconnected?.Invoke(this, e);
        }

        protected virtual void OnDeviceIdentified(DeviceIdentifiedEventEventArgs e)
        {
            this.DeviceIdentified?.Invoke(this, e);
        }

        protected virtual void OnDeviceReconncted(DeviceConnectionEventArgs e)
        {
            this.DeviceReconnected?.Invoke(this, e);
        }

        public string GetConnectionId(string deviceId)
        {
            return Store[deviceId].CurrentConnectionId;
        }

        public string[] GetAppClientDeviceIds(string appId)
        {
            return Store.Where(kvp => kvp.Value.AppId == appId).Select(kv => kv.Value.DeviceId).ToArray();
        }

        public DeviceStatus GetDeviceStatus(string deviceId)
        {
            return Store.ContainsKey(deviceId) ? DeviceStatus.Online : DeviceStatus.Offline;
        }

        public void Disconnect(string connectionId)
        {
            var item = Store.FirstOrDefault(c => c.Value.CurrentConnectionId == connectionId).Value;
            if (item != null)
            {
                Store.Remove(item.DeviceId);
                this.OnDeviceDisconnected(new DeviceConnectionEventArgs() { ConnectionId = connectionId, DeviceId = item.DeviceId });
            }
        }

        public void SetDeviceInfo(string deviceId, ClientInfo info)
        {
            if (!Store.ContainsKey(deviceId))
            {
                Store[deviceId] = info;
            }
            this.OnDeviceIdentified(new DeviceIdentifiedEventEventArgs() { ClientInfo = info });
        }

        
        #endregion

        // TODO: complete event handlers for connection manager (right it dose not raise connected, reconnected)..
        #region EventHandler
        
        public class ConnectedHandler : IConnectionEventHandler
        {
            public ConnectionEventTypes Type
            {
                get
                {
                    return ConnectionEventTypes.OnConnected;
                }
            }

            public void Execute(HubCallerContext context, ClientInfoDTO info)
            {
                
            }
        }

        public class DisconnectedHandler : IConnectionEventHandler
        {
            public ConnectionEventTypes Type
            {
                get
                {
                    return ConnectionEventTypes.OnDisconnected;
                }
            }


            public void Execute(HubCallerContext context, ClientInfoDTO info)
            {
                Current.Disconnect(context.ConnectionId);
            }
        }


        public class ReconnectedHandler : IConnectionEventHandler
        {
            public ConnectionEventTypes Type
            {
                get
                {
                    return ConnectionEventTypes.OnReconnected;
                }
            }

            public void Execute(HubCallerContext context, ClientInfoDTO info)
            {
            }
        }

        public class IdentifiedHandler : IConnectionEventHandler
        {
            public ConnectionEventTypes Type
            {
                get
                {
                    return ConnectionEventTypes.DeviceIdentified;
                }
            }

            public void Execute(HubCallerContext context, ClientInfoDTO info)
            {

                Current.SetDeviceInfo(info.DeviceId, new ClientInfo()
                {
                    AppId = info.AppId,
                    DeviceId = info.DeviceId,
                    CurrentConnectionId = context.ConnectionId
                });
            }
        }
        #endregion
    }
}
